package cbaApplication;

import observer.ObserverPubText;
import observer.ObserverPubImage;
import observer.LogSubject;
import observer.IObserver;
import commonSources.Cliente;
import java.rmi.RemoteException;
import cbaServer.BankServerCBAInterface;
import java.rmi.ConnectException;
import java.rmi.Naming;
import java.text.SimpleDateFormat;
import java.util.Vector;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import operacoes2.Carregamento;
import operacoes2.Deposito;
import operacoes2.Levantamento;
import operacoes2.MudarPIN;
import operacoes2.Operacao;
import operacoes2.Pagamento;
import operacoes2.Transferencia;
import operacoes2.Vazadeposito;
import state.StateContext;

// <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
// #[regen=yes,id=DCE.1FD9C852-7A2F-73C3-D4EC-18F2E7DFE82D]
// </editor-fold> 
public class CBA {

    private BankServerCBAInterface bankServerIf;
    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.170C5490-FD15-4697-5875-CE68E9DC2E37]
    // </editor-fold> 
    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.3D4A9458-6740-1F82-B3B5-E951B5C5E64C]
    // </editor-fold> 
    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.E66FF134-A943-EA55-A24D-21909EA720F1]
    // </editor-fold> 
    private Vector<Operacao> listaOperacoes;
    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.D7BA5831-E825-2E6B-AB8F-C883778D6B82]
    // </editor-fold>
    private Files files;
    private Cliente cliente;
    private String idConta;
    private String numCard;
    private EnviaResumoThread ert;
    private double totalLev = 5000;
    private double totalDepositos;
    private final SimpleDateFormat dateFormat;
    private StateContext sc;
    private final CbaUI ui;
    private boolean connected = false;
    private final LogSubject subject;

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.BBF13232-A1B4-789D-AE7F-A3F19A19AD1E]
    // </editor-fold> 
    public CBA() {
        files = Files.getInstance();
        if (!criaLigacaoRMI()) {
            System.exit(0);
        }
        dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        this.listaOperacoes = files.loadOperacoes();
        double[] valores = files.loadValores();
        this.totalLev = valores[0];
        this.totalDepositos = valores[1];
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            System.out.println("Não foi possível alterar a L&F");
        }
        ui = new CbaUI(this);
        ui.setVisible(true);
        ert = new EnviaResumoThread(this);
        ert.start();
        printAllOps();
        //Observers attach
        subject = new LogSubject();
        IObserver obsText = new ObserverPubText(this);
        IObserver obsImage = new ObserverPubImage(this);
        subject.attach(obsText);
        subject.attach(obsImage);

    }

    public static void main(String[] args) {
        CBA cba = new CBA();
    }

    private boolean criaLigacaoRMI() {
        System.out.println("RMI create");
        try {
            String rmiAddress = "rmi://localhost/bankserverCBA";
            bankServerIf = (BankServerCBAInterface) Naming.lookup(rmiAddress);
            System.out.println("## objecto remoto criado ##");
            setSc(new StateContext(this.getBankServerIf()));
            getSc().setState(true);
            setConnected(true);
            Vector<Levantamento> levantamentos = files.getBackup();
            if (levantamentos.size() > 0) {
                System.out.println("levantamentos em backup: " + levantamentos.size());
                for (Levantamento operacao : levantamentos) {
                    if (bankServerIf.levantamento(operacao.getNumConta(), operacao.getQuantia()) == 0) {
                        break;
                    }
                }
                files.deleteBackups();
            } else {
                System.out.println("backup vazio");
            }
            return true;
        } catch (Exception e) {
            //e.printStackTrace();
            System.out.println("## Servidor não atingível ##");
            if (bankServerIf != null) {
                getSc().setState(false);
            }
            return false;
        }
    }

    public int autentica(String numCard, String pin) {

        try {
            idConta = getBankServerIf().autentica(numCard, pin);
            setConnected(true);
            if (idConta == null) {
                return 0;
            } else if (idConta.equals("funcionario")) {
                this.numCard = numCard;
                return 1;
            } else {
                this.numCard = numCard;
                cliente = getBankServerIf().getClienteByNumCard(numCard);
                if (cliente != null) {
                    subject.setState(cliente.getPainel());
                } else {
                    System.out.println("Cliente é null");
                }
                return 2;
            }

        } catch (RemoteException ex) {
            getSc().setState(false);
            setConnected(false);
            return -1;

        }
    }

    public double consultaSaldo() {
        if (!isConnected()) {
            System.out.println("DISCONNECTED SALDO");
            criaLigacaoRMI();
        }
        try {
            return getBankServerIf().consultaSaldo(idConta);
        } catch (RemoteException ex) {
            //ex.printStackTrace();
            JOptionPane.showMessageDialog(null, "Impossível contactar o servidor");

            getSc().setState(false);
            setConnected(false);
            return 1.111;
        }
    }

    public int levantamento(double quantia) {
        int ret = 9;
        if (quantia < totalLev) {
            if ((ret = fazLevantamento(quantia)) == -7) {
                System.out.println(":::::ASA");
                return fazLevantamento(quantia);
            }
        }
        return ret;
    }

    private int fazLevantamento(double quantia) {
        int res = getSc().fazLevantamento(idConta, quantia);
        System.out.println("RES: " + res);
        if (res == 1) {
            listaOperacoes.add(new Levantamento(idConta, quantia));
            totalLev -= quantia;
            getFiles().saveOperacoes(listaOperacoes);
            getFiles().saveValores(totalLev, totalDepositos);
        } else if (res == -1) {
            System.out.println("Levantamento cant connect");
            setConnected(false);
            getSc().setState(false);
            res = -7;
        } else if (res == -56) {
            JOptionPane.showMessageDialog(null, "Não pode levantar mais de 100€");
        }
        return res;
    }

    public int deposito(double quantia) {
        if (!isConnected()) {
            criaLigacaoRMI();
        }
        try {
            if (bankServerIf.deposito(idConta, quantia)) {
                listaOperacoes.add(new Deposito(idConta, quantia));
                totalDepositos += quantia;
                getFiles().saveOperacoes(listaOperacoes);
                getFiles().saveValores(totalLev, totalDepositos);
                getSc().setState(true);
                setConnected(true);
                return 1;
            } else {
                getSc().setState(true);
                setConnected(true);
                return 0;
            }
        } catch (RemoteException ex) {
            JOptionPane.showMessageDialog(null, "Impossível contactar o servidor");
            getSc().setState(false);
            setConnected(false);
            return -1;
        }
    }

    public int mudaPIN(String pin) {
        if (!isConnected()) {
            criaLigacaoRMI();
        }
        try {
            boolean res = bankServerIf.mudaPIN(idConta, numCard, pin);
            System.out.println("Client muda PIN");
            if (res) {
                listaOperacoes.add(new MudarPIN(idConta, numCard));
                getFiles().saveOperacoes(listaOperacoes);
                getSc().setState(true);
                setConnected(true);
                return 1;
            }
            getSc().setState(true);
            setConnected(true);
            return 0;
        } catch (RemoteException ex) {
            JOptionPane.showMessageDialog(null, "Impossível contactar o servidor");
            System.out.println("mudaPIN FAILED");
            getSc().setState(false);
            setConnected(false);
            return -1;
        }
    }

    public int transferencia(String idDest, double quantia) {
        if (!isConnected()) {
            criaLigacaoRMI();
        }
        try {
            int res = bankServerIf.transferencia(idConta, idDest, quantia);
            if (res == 1) {
                listaOperacoes.add(new Transferencia(idConta, idDest, quantia));
                getFiles().saveOperacoes(listaOperacoes);
            }
            getSc().setState(true);
            setConnected(true);
            return res;
        } catch (RemoteException ex) {
            JOptionPane.showMessageDialog(null, "Impossível contactar o servidor");
            getSc().setState(false);
            System.out.println("Transf FAILED");
            setConnected(false);
            return -1;
        }
    }

    public int pagamento(String ref, double quantia) {
        if (!isConnected()) {
            criaLigacaoRMI();
        }
        try {
            int res = bankServerIf.pagamentoServico(idConta, ref, quantia);
            if (res == 1) {
                listaOperacoes.add(new Pagamento(idConta, ref, quantia));
                getFiles().saveOperacoes(listaOperacoes);
            }
            getSc().setState(true);
            return res;
        } catch (RemoteException ex) {
            getSc().setState(false);
            JOptionPane.showMessageDialog(null, "Impossível contactar o servidor");
            System.out.println("pagamento FAILED");
            return -1;
        }
    }

    public double getTotalDinheiro() {
        return totalLev;
    }

    public void setTotalDinheiro(double totalDinheiro) {
        this.totalLev = totalDinheiro;
    }

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,regenBody=yes,id=DCE.CE7B7100-CCD6-0964-40AF-ABACDD731384]
    // </editor-fold> 
    public Vector<Operacao> getListaOperacoes() {
        return listaOperacoes;
    }

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,regenBody=yes,id=DCE.86F0EDC2-BFD1-5D96-918D-EB2B513E7ABA]
    // </editor-fold> 
    public void setListaOperacoes(Vector<Operacao> val) {
        this.listaOperacoes = val;
    }

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.7C7AEBC4-A866-A2B9-C751-2A3CFBB3352D]
    // </editor-fold> 
    public void adicionaOperacao(Operacao oper) {
    }

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.EB6FD598-BE8C-0454-0F93-F094A9F477D7]
    // </editor-fold> 
    public void removeOperacao(Operacao oper) {
    }

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,regenBody=yes,id=DCE.11B288C0-AA5A-E10E-BD98-9E8CD40524DC]
    // </editor-fold> 
    public Cliente getCliente() {
        return cliente;
    }

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,regenBody=yes,id=DCE.99C42E25-8B2F-A92C-ED93-F052D1483710]
    // </editor-fold> 
    public void setCliente(Cliente val) {
        this.cliente = val;
    }

    /**
     * @return the bankServerIf
     */
    public BankServerCBAInterface getBankServerIf() {
        return bankServerIf;
    }

    public double getTotalDepositos() {
        return totalDepositos;
    }

    public void setTotalDepositos(double totalDepositos) {
        this.totalDepositos = totalDepositos;
    }

    void carregaMaquina(double valor) {
        totalLev += valor;
        listaOperacoes.add(new Carregamento(numCard, valor));
        getFiles().saveOperacoes(listaOperacoes);
        getFiles().saveValores(totalLev, totalDepositos);
    }

    void retiraDepositos() {
        totalDepositos = 0;
        listaOperacoes.add(new Vazadeposito(numCard));
        getFiles().saveOperacoes(listaOperacoes);
        getFiles().saveValores(totalLev, totalDepositos);
    }

    private void printAllOps() {
        for (Operacao operacao : listaOperacoes) {
            System.out.println(operacao.getClass().getCanonicalName());
            System.out.println(dateFormat.format(operacao.getData()));
        }
    }

    /**
     * @return the files
     */
    public Files getFiles() {
        return files;
    }

    /**
     * @param files the files to set
     */
    public void setFiles(Files files) {
        this.files = files;
    }

    void refreshOperacoes() {
        listaOperacoes.clear();
        files.saveOperacoes(listaOperacoes);
    }

    /**
     * @return the ui
     */
    public CbaUI getUi() {
        return ui;
    }

    /**
     * @return the sc
     */
    public StateContext getSc() {
        return sc;
    }

    /**
     * @param sc the sc to set
     */
    public void setSc(StateContext sc) {
        this.sc = sc;
    }

    /**
     * @return the connected
     */
    public boolean isConnected() {
        return connected;
    }

    /**
     * @param connected the connected to set
     */
    public void setConnected(boolean connected) {
        this.connected = connected;
    }
}

